/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package org.jfonia.view.symbols;

import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import org.jfonia.connect5.basics.BasicValueNode;
import org.jfonia.connect5.basics.MutableValueNode;
import org.jfonia.connect5.basics.ValueNode;
import org.jfonia.connect5.basics.Observer;
import org.jfonia.connect5.intervals.BasicDoubleInterval;
import org.jfonia.connect5.intervals.LinkedDoubleIntervalList;
import org.jfonia.connect5.relations.Equal;
import org.jfonia.connect5.relations.UnidirectionalEqual;
import org.jfonia.constants.SymbolConstants;
import org.jfonia.model.elements.TimeSignature;

/**
 *
 * @author Rik Bauwens
 */
public class TimeSignatureSymbol extends ComplexStaffSymbol
{
        private TimeSignature timeSignature;

        private IStaffSymbol[] nominatorSymbols;
        private IStaffSymbol[] denominatorSymbols;

        private BasicDoubleInterval nominatorOffset;
        private BasicDoubleInterval denominatorOffset;

        public TimeSignatureSymbol(ValueNode<Double> frameX, ValueNode<Double> staffY,
                          MutableValueNode<Double> staffBase, MutableValueNode<Double> postStaffBase,
                          ValueNode<Double> xNode, MutableValueNode<Double> widthNode,
                          TimeSignature timeSignature)
        {
                super(frameX, staffY, staffBase, postStaffBase, xNode, widthNode, 
                        new BasicValueNode<Integer>((SymbolConstants.NOMINATOR_RANK
                                              + SymbolConstants.DENOMINATOR_RANK) / 2));
                this.timeSignature = timeSignature;
                
                //Auxiliary intervals to center the numbers
                nominatorOffset = new BasicDoubleInterval();
                new UnidirectionalEqual<Double>(xNode, nominatorOffset.getBeginNode());
                denominatorOffset = new BasicDoubleInterval();
                new UnidirectionalEqual<Double>(xNode, denominatorOffset.getBeginNode());

                init(frameX, staffY, staffBase, postStaffBase);
        }

        private void init(ValueNode<Double> frameX, ValueNode<Double> staffY,
                          MutableValueNode<Double> staffBase, MutableValueNode<Double> postStaffBase)
        {
                //Initialize bounds
                rectangle = new Rectangle2D.Double(getX(), getY(), 0, 0);

                //Nominator
                nominatorSymbols = makeNumber(nominatorOffset.getEndNode(), 
                        SymbolConstants.NOMINATOR_RANK, timeSignature.getNominator());
                rectangle.add(getNominatorBounds());

                //Denominator
                denominatorSymbols = makeNumber(denominatorOffset.getEndNode(), 
                        SymbolConstants.DENOMINATOR_RANK, timeSignature.getDenominator());
                rectangle.add(getDenominatorBounds());

                //Center the numbers
                centerNumbers();

                //Update nodes
                updateNodes();
                
                //Add observers
                timeSignature.getNominatorNode().addObserver(new Observer()
                {
                        public void onNotify(Object source)
                        {
                                //Create new bounds
                                rectangle = new Rectangle2D.Double(getX(), getY(), 0, 0);
                                //Recalculate nominator
                                nominatorSymbols = makeNumber(nominatorOffset.getEndNode(),//getXNode(),
                                        SymbolConstants.NOMINATOR_RANK,
                                        getTimeSignature().getNominator());
                                rectangle.add(getNominatorBounds());
                                //Add bounds from denominator
                                rectangle.add(getDenominatorBounds());
                                //Center numbers
                                centerNumbers();
                                //Update the nodes
                                updateNodes();
                        }
                });
                timeSignature.getDenominatorNode().addObserver(new Observer()
                {
                        public void onNotify(Object source)
                        {
                                //Create new bounds
                                rectangle = new Rectangle2D.Double(getX(), getY(), 0, 0);
                                //Recalculate denominator
                                denominatorSymbols = makeNumber(denominatorOffset.getEndNode(),//getXNode(),
                                        SymbolConstants.DENOMINATOR_RANK,
                                        getTimeSignature().getDenominator());
                                rectangle.add(getDenominatorBounds());
                                //Add bounds from nominator
                                rectangle.add(getNominatorBounds());
                                //Center numbers
                                centerNumbers();
                                //Update the nodes
                                updateNodes();
                        }
                });
        }

        private TimeSignature getTimeSignature()
        {
                return timeSignature;
        }

        //numbers that exceed 9
        private IStaffSymbol[] makeNumber(MutableValueNode<Double> xNode, int rank, int number)
        {
                //assert(number>0);
                int digitCount = 0, xx = number;
                while(xx > 0)
                {
                        digitCount++;
                        xx /= 10;
                }
                IStaffSymbol[] res = new DigitSymbol[digitCount];

                LinkedDoubleIntervalList numberWidth = new LinkedDoubleIntervalList();
                new Equal<Double>(xNode, numberWidth.getBeginNode());
                for(int i = digitCount - 1; i >= 0; i--)
                {
                        BasicDoubleInterval digitWidth = new BasicDoubleInterval();
                        //Draw symbol in front of the other symbols
                        numberWidth.insert(0, digitWidth);
                        res[i] = new DigitSymbol(getFrameX(), getStaffY(), getStaffBase(), getPostStaffBase(),
                                                 digitWidth.getBeginNode(), digitWidth.getDifferenceNode(),
                                                 new BasicValueNode<Integer>(rank), number % 10);
                        number /= 10;
                }
                return res;
        }

        private void centerNumbers()
        {
                double nominatorWidth = getNominatorBounds().getWidth();
                double denominatorWidth = getDenominatorBounds().getWidth();
                if(nominatorWidth < denominatorWidth)
                {
                        nominatorOffset.setDifference((denominatorWidth - nominatorWidth) / 2);
                        denominatorOffset.setDifference(0);
                }
                else if(nominatorWidth > denominatorWidth)
                {
                        nominatorOffset.setDifference(0);
                        denominatorOffset.setDifference((nominatorWidth - denominatorWidth) / 2);
                }
        }

        private Rectangle2D getNominatorBounds()
        {
                return getBounds(nominatorSymbols);
        }

        private Rectangle2D getDenominatorBounds()
        {
                return getBounds(denominatorSymbols);
        }

        //Prevent that the rank can be changed (fixed)
        @Override
        public void setRank(int rank) {}

        @Override
        public void paintSymbol(Graphics2D g)
        {
                super.paintSymbol(g);
                for(IStaffSymbol nominatorSymbol : nominatorSymbols)
                        nominatorSymbol.paintSymbol(g);
                for(IStaffSymbol denominatorSymbol : denominatorSymbols)
                        denominatorSymbol.paintSymbol(g);
        }
}
